package com.learn.security.config;

import com.learn.security.component.AuthenticationEntryPointImpl;
import com.learn.security.component.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author : Yu.Tan
 * @date : 2022/12/8 13:55
 * @desc : **
 * Java Config 配置的权限，和注解配置的权限，两者是叠加的。
 * @EnableGlobalMethodSecurity(prePostEnabled = true)  开启对 Spring Security 注解的方法，进行权限验证
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 重写 #authenticationManagerBean 方法，解决无法直接注入 AuthenticationManager 的问题
     * 额外添加了 @Bean 注解，保证创建出 AuthenticationManager Bean
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }

    /**
     * 身份认证接口
     * UserDetailsService 实现类，更加灵活且自由的实现认证的用户信息的读取
     * 设置 PasswordEncoder 密码编码器
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(bCryptPasswordEncoder());
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers("/v1/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .and()
                .addFilterBefore(jwtAuthenticationTokenFilter(),UsernamePasswordAuthenticationFilter.class)
                // 打开Spring Security的跨域
                .cors()
                .and()
                // 关闭CSRF
                .csrf().disable()
                // 关闭Session机制
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


//        http
//                // CRSF禁用，因为不使用session
//                .csrf().disable()
//                // <X> 认证失败处理类
//                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
//                // 基于token，所以不需要session
//                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
//                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
//                // 过滤请求
//                .authorizeRequests()
//                // <Y> 对于登录login 验证码captchaImage 允许匿名访问
//                .antMatchers("/login","/captchaImage").anonymous()
//                .antMatchers(HttpMethod.GET,
//                        "/*.html",
//                        "/**/*.html",
//                        "/**/*.css",
//                        "/**/*.js").permitAll()
//                .antMatchers("/profile/**").anonymous()
//                .antMatchers("/common/download**").anonymous()
//                .antMatchers("/swagger-ui.html").anonymous()
//                .antMatchers("/swagger-resources/**").anonymous()
//                .antMatchers("/webjars/**").anonymous()
//                .antMatchers("/*/api-docs").anonymous()
//                .antMatchers("/druid/**").anonymous()
//                // 除上面外的所有请求全部需要鉴权认证
//                .anyRequest().authenticated()
//                .and()
//                .headers().frameOptions().disable();
        // <P> 添加 JWT filter

    }


    /**
     * AuthenticationManager 认证管理器
     *
     * @param auth
     * @throws Exception
     */
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth
//                // <X>  提供认证的用户信息。 使用内存中的 InMemoryUserDetailsManager
//                .inMemoryAuthentication()
//                // <Y>  不使用 PasswordEncoder 密码编码器
//                .passwordEncoder(NoOpPasswordEncoder.getInstance())
//                // <Z> 配置 admin 用户
//                .withUser("admin").password("admin").roles("ADMIN")
//                .and()
//                // <Z> 配置 normal 用户
//                .withUser("normal").password("normal").roles("NORMAL");
//    }

    /**
     * 配置 URL 的权限控制
     * #(String... antPatterns) 方法，配置匹配的 URL 地址，基于 Ant 风格路径表达式 ，可传入多个
     * 【常用】#permitAll() 方法，所有用户可访问。
     * 【常用】#denyAll() 方法，所有用户不可访问
     * 【常用】#authenticated() 方法，登录用户可访问。
     * #anonymous() 方法，无需登录，即匿名用户可访问。
     * #rememberMe() 方法，通过 remember me 登录的用户可访问。
     * #fullyAuthenticated() 方法，非 remember me 登录的用户可访问。
     * #hasIpAddress(String ipaddressExpression) 方法，来自指定 IP 表达式的用户可访问。
     * 【常用】#hasRole(String role) 方法， 拥有指定角色的用户可访问。
     * 【常用】#hasAnyRole(String... roles) 方法，拥有指定任一角色的用户可访问。
     * 【常用】#hasAuthority(String authority) 方法，拥有指定权限(authority)的用户可访问。
     * 【常用】#hasAuthority(String... authorities) 方法，拥有指定任一权限(authority)的用户可访问。
     * 【最牛】#access(String attribute) 方法，当 Spring EL 表达式的执行结果为 true 时，可以访问。
     *
     * @param http
     * @throws Exception
     */
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http
//                // <X> 配置请求地址的权限
//                .authorizeRequests()
//                // 所有用户可访问
//                .antMatchers("/test/echo").permitAll()
//                // 需要 ADMIN 角色
//                .antMatchers("/test/admin").hasRole("ADMIN")
//                // 需要 NORMAL 角色。
//                .antMatchers("test/normal").hasRole("ADMIN")
//                // 任何请求，访问的用户都需要经过认证
//                .anyRequest().authenticated()
//                .and()
//                // <Y> 设置 Form 表单登录
//                .formLogin()
//                // 登录 URL 地址
////                .loginPage("/login")
//                // 所有用户可访问
//                .permitAll()
//                .and()
//                // 配置退出相关
//                .logout()
//                // 退出 URL 地址
////                .logoutUrl("/logout")
//                // 所有用户可访问
//                .permitAll();
//    }
}
